package bupt.xixi.codec;

import bupt.xixi.entity.RpcRequest;
import bupt.xixi.entity.RpcResponse;
import bupt.xixi.enumeration.Codec;
import bupt.xixi.enumeration.RpcError;
import bupt.xixi.exception.RpcException;
import bupt.xixi.serializer.ICommonSerializer;
import bupt.xixi.serializer.SerializerFactory;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * 通用解码器
 * ReplayingDecoder继承了ByteToMessageDecoder，但是使用ReplayingDecoder的好处在于：
 * ReplayingDecoder在处理数据时可以认为所有的数据（ByteBuf） 已经接收完毕，而不用判断接收数据的长度
 *
 * +---------------+---------------+-----------------+-------------+
 * |  Magic Number |  Package Type | Serializer Type | Data Length |
 * |    4 bytes    |    4 bytes    |     4 bytes     |   4 bytes   |
 * +---------------+---------------+-----------------+-------------+
 * |                          Data Bytes                           |
 * |                   Length: ${Data Length}                      |
 * +---------------------------------------------------------------+
 */
public class CommonDecoder extends ReplayingDecoder {

    private static final Logger logger = LoggerFactory.getLogger(CommonDecoder.class);


    @Override
    protected void decode(ChannelHandlerContext context, ByteBuf byteBuf, List<Object> list) throws Exception {
        int magic = byteBuf.readInt();
        Class<?> packageClass;
        // 1. 检查魔数Magic Number
        if (magic != Codec.MAGIC_NUMBER) {
            logger.error("无法识别的数据包: {}", magic);
            throw new RpcException(RpcError.UNKNOWN_PROTOCOL);
        }
        // 2. 检查Package Type
        int packageCode = byteBuf.readInt();
        if (packageCode == Codec.PackageType.REQUEST_PACK.getCode()) {
            // 2.1 如果是requset请求
            packageClass = RpcRequest.class;
        } else if (packageCode == Codec.PackageType.RESPONSE_PACK.getCode()) {
            // 2.2 如果是response请求
            packageClass = RpcResponse.class;
        } else if (packageCode == Codec.PackageType.HEART_PACK.getCode()){
            // 2.3 如果是心跳请求
            handlerHeart(byteBuf, context);
            return;
            // packageClass = String.class;
        } else {
            // 2.3 如果一个都不是
            logger.error("无法识别的数据包: {}", packageCode);
            throw new RpcException(RpcError.UNKNOWN_PACKAGE_TYPE);
        }
        // 3. 检查序列器与反序列器
        int serializerCode = byteBuf.readInt();
        ICommonSerializer serializer = SerializerFactory.getByCode(serializerCode);
        if (serializer == null) {
            logger.error("不识别的反序列化器: {}", serializerCode);
            throw new RpcException(RpcError.UNKNOWN_SERIALIZER);
        }
        // 4. 读取数据长度
        int length = byteBuf.readInt();
        // 5. 生成该长度的byte接数据
        byte[] bytes = new byte[length];
        byteBuf.readBytes(bytes);
        Object obj = serializer.deserialize(bytes, packageClass);
        // channelHandlerContext会遍历list里的每个元素, 让与之适配的ChannelInboundHandler来处理这个解码后的数据
        list.add(obj);
    }

    private void handlerHeart(ByteBuf byteBuf, ChannelHandlerContext ctx) {
        // 还是想包装好成为rpcRequest统一处理
        final int heartCode = byteBuf.readInt();
        // 如果发现是PING~返回一个PONG~
        if (heartCode == Codec.heartData.PING.getCode()) {
            logger.info("收到客户端发来的心跳PING~返回一个PONG");
            final Channel channel = ctx.channel();
            channel.write(Codec.MAGIC_NUMBER);
            channel.write(Codec.PackageType.HEART_PACK.getCode());
            channel.write(Codec.heartData.PONG.getCode());
            channel.flush();
        }
        // 如果发现是PONG~就不理了~
        if (heartCode == Codec.heartData.PONG.getCode()) {
            logger.info("收到服务端回复的PONG~");
        }
    }


}
